fix(agent): 明确 pending 消息消费决策#810
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
Walkthrough更新代理执行器以更精准处理工具的直接输出(含 Changes
Sequence Diagram(s)sequenceDiagram
participant AgentExec as AgentExecutor
participant ToolRT as ToolRuntime
participant HumanQ as HumanUpdateQueue
participant Events as EventEmitter
AgentExec->>ToolRT: execute(action)
ToolRT-->>AgentExec: observation (可能含 replyEmitted)
alt observation 为直接输出 (returnDirect 或 isDirectToolOutput)
AgentExec->>Events: emit round-decision(canContinue:false)
AgentExec->>HumanQ: drain pending human-update -> emit human-update events
alt observation.replyEmitted == true
AgentExec->>Events: emit done { output: "", replyEmitted:true }
else
AgentExec->>Events: emit done { output: toOutput(observation), replyEmitted:false }
end
else 常规工具输出
AgentExec->>Events: emit round-decision(canContinue:true)
AgentExec->>AgentExec: iterations++
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 分钟 可能相关的 PRs
诗歌
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
959b58e to
70814bb
Compare
There was a problem hiding this comment.
Code Review
This pull request renames the canContinue property to willConsumePendingMessages across the agent execution and chat service logic to more accurately describe its function. It also introduces a mechanism to handle final character replies emitted by tools, adding a replyEmitted flag to the done event and updating the UI to reflect when a reply has been sent by a tool. Furthermore, the Docker configuration guide for the extension agent has been updated with recommendations for setting home and working directories, and the parameter descriptions for the group mute tool have been refined for better clarity. I have no feedback to provide as there were no review comments to evaluate.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/core/src/llm-core/agent/legacy-executor.ts (1)
317-335:⚠️ Potential issue | 🔴 Critical直接工具输出分支在协化阶段后无法触发,Line 333 的输出也会被重新序列化。
Line 317 的检查
isDirectToolOutput(last.observation)无法命中,因为last.observation在 Line 93-101 的coerceToAgentObservation()中已经被处理。该函数的isAgentObservation()内部判断仅接受字符串或 MessageContentComplex 数组;DirectToolOutput 对象(作为普通对象)都不符合这些条件,因此被JSON.stringify()转换成字符串。到达 Line 317 时,观察值已是字符串,isDirectToolOutput()返回 false,分支不可达。即使分支能被触发,Line 333 的
toOutput()仍会对这个值走字符串化路径,而不是提取真正的直接工具输出内容,导致基于 DirectToolOutput 的终止逻辑失效。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/llm-core/agent/types.ts`:
- Around line 197-200: AgentObservation now includes DirectToolOutput but
consumers assume string/array; add type guards to handle DirectToolOutput
explicitly: where code reads the observation variable (e.g., the length/null
checks and string interpolation using observation and observationPrefix, and
places that feed ToolMessage content), first call
isDirectToolOutput(observation) (or inline check for the DirectToolOutput shape)
and either convert it to a string/appropriate ToolMessage content via the same
serialization used by coerceToAgentObservation or branch to handle its fields,
ensuring you don't access observation.length or interpolate the object directly;
update both the observation length/null check site and the buffer +
`\n${observationPrefix}${observation}\n` concatenation to guard/transform
DirectToolOutput before use.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b0300d7d-6b2e-421e-aa7e-750664297aee
📒 Files selected for processing (4)
packages/core/src/llm-core/agent/legacy-executor.tspackages/core/src/llm-core/agent/sub-agent.tspackages/core/src/llm-core/agent/types.tspackages/core/src/services/chat.ts
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/adapter-gemini/src/utils.ts (1)
211-220:⚠️ Potential issue | 🟠 Major修复
gemma模型的图片处理逻辑不一致导致的数据丢弃问题
packages/shared-adapter/src/client.ts第 152 行的imageModelMatchers包含'gemma'模式,导致supportImageInput('gemma-7b')返回true,上游代码会为这些模型构造包含图片的请求。但本文件第 215 行的条件检查
model.includes('gemma2'),使得普通gemma模型的图片被静默丢弃,用户不会收到任何提示或错误。同时,
packages/adapter-gemini/src/requester.ts第 145 行仍按'gemma'关键字过滤模型列表,表明适配器预期支持普通gemma模型。必须统一两处逻辑:
- 若 Gemini API 仅
gemma2系列支持图片,则应在shared-adapter中将'gemma'改为'gemma2'- 若
gemma-7b等普通 gemma 模型应支持图片,则需将第 215 行改回检查'gemma'两种方案都可行,但当前状态会导致静默数据丢失。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/adapter-gemini/src/utils.ts` around lines 211 - 220, The model-image support check in the condition using model.includes('gemma2') is inconsistent with imageModelMatchers/supportImageInput which include 'gemma' and with requester.ts which filters by 'gemma', causing gemma models' images to be dropped; update the condition in the function containing this snippet to check for 'gemma' as well (e.g., include model.includes('gemma') alongside model.includes('gemma2') or replace 'gemma2' with 'gemma' to match the rest of the adapter), keep the existing exclusion for 'gemini-1.0', and run relevant tests or verify supportImageInput and requester.ts behaviors remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/middlewares/chat/stop_chat.ts`:
- Around line 84-87: The code is incorrectly re-checking request ownership by
calling ctx.chatluna.conversationRuntime.getRequestId(session, conversation.id),
which narrows stop permissions back to the original requester; instead remove
the session-based lookup and call stopConversationRequest with the conversation
id directly (use conversation.id as the identifier passed to
ctx.chatluna.conversationRuntime.stopConversationRequest), delete the requestId
variable usage, and keep the earlier permission: 'manage' semantics so
admins/shared managers can stop others' active requests.
In `@packages/core/src/services/conversation_runtime.ts`:
- Around line 449-478: dispose() aborts active requests then immediately removes
entries from activeByConversation, which leaves appendPendingMessage() Promises
waiting on roundDecisionResolvers unresolved; before deleting an active you must
either resolve its pending roundDecisionResolvers (resolve(false)) or call
completeRequest(active.conversationId, active.requestId) so the pending promise
can drain. Update dispose(platform?) to, for each matching active, first
clear/resolve the roundDecisionResolvers for that active (or invoke
completeRequest(active.conversationId, active.requestId)), then call
active.abortController.abort(...) and only after that delete from
activeByConversation and interfaces to avoid permanently hanging
appendPendingMessage() callers.
In `@packages/core/src/services/conversation.ts`:
- Around line 1828-1834: The current logic always defaults
constraint.users/constraint.excludeUsers to '[]' so an explicit null (meaning
"no restriction") is treated as an empty list and incorrectly blocks everyone;
update the checks so you first test whether constraint.users === null (in which
case skip the inclusion test), otherwise JSON.parse(constraint.users) into users
and then run the existing users != null && !users.includes(session.userId)
check; do the analogous change for constraint.excludeUsers (treat null as "no
excludes", only JSON.parse when not null) so the variables users and
excludeUsers reflect the intent of a null value rather than being coerced to [].
In `@packages/shared-adapter/src/client.ts`:
- Line 152: The supportImageInput list currently includes 'gemma' but
adapter-gemini's processImageParts only handles models containing 'gemma2',
causing images to be silently dropped; either remove 'gemma' from the
supportImageInput set or update processImageParts in
packages/adapter-gemini/src/utils.ts to also handle 'gemma' (or log a warning
when a model matches 'gemma' but not 'gemma2'); locate the supportImageInput
declaration in client.ts and the processImageParts function in adapter-gemini's
utils.ts and make the model-name checks consistent (or add a clear warning log
in processImageParts when an unsupported gemma variant is passed).
- Line 132: The fallback hardcoded return value in getModelMaxContextSize
(currently "return 200_000") increases the default context window for unmapped
models and can break token/counting logic downstream; change it to avoid a large
hardcoded value—either restore the previous fallback to use
getModelContextSize('o1-mini') or derive the fallback from
modelMaxContextSizeTable/configuration (or expose a configurable
DEFAULT_MODEL_MAX_CONTEXT_SIZE) so unknown models use the conservative prior
value; update getModelMaxContextSize and any callers that assume the smaller
default and add a short comment documenting the chosen fallback behavior.
---
Outside diff comments:
In `@packages/adapter-gemini/src/utils.ts`:
- Around line 211-220: The model-image support check in the condition using
model.includes('gemma2') is inconsistent with
imageModelMatchers/supportImageInput which include 'gemma' and with requester.ts
which filters by 'gemma', causing gemma models' images to be dropped; update the
condition in the function containing this snippet to check for 'gemma' as well
(e.g., include model.includes('gemma') alongside model.includes('gemma2') or
replace 'gemma2' with 'gemma' to match the rest of the adapter), keep the
existing exclusion for 'gemini-1.0', and run relevant tests or verify
supportImageInput and requester.ts behaviors remain consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b89fe3ae-b879-4efe-8a6a-d12be36626de
📒 Files selected for processing (16)
packages/adapter-gemini/src/utils.tspackages/core/src/llm-core/agent/legacy-executor.tspackages/core/src/llm-core/agent/sub-agent.tspackages/core/src/llm-core/agent/types.tspackages/core/src/llm-core/platform/service.tspackages/core/src/middlewares/chat/stop_chat.tspackages/core/src/middlewares/conversation/request_conversation.tspackages/core/src/services/chat.tspackages/core/src/services/conversation.tspackages/core/src/services/conversation_runtime.tspackages/core/src/services/message_transform.tspackages/core/src/services/types.tspackages/core/tests/conversation-runtime.spec.tspackages/extension-agent/src/service/permissions.tspackages/extension-agent/src/service/sub_agent.tspackages/shared-adapter/src/client.ts
✅ Files skipped from review due to trivial changes (2)
- packages/extension-agent/src/service/sub_agent.ts
- packages/core/src/services/message_transform.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/llm-core/agent/sub-agent.ts
- packages/core/src/llm-core/agent/types.ts
- packages/core/src/llm-core/agent/legacy-executor.ts
Summary
canContinue明确为willConsumePendingMessages,让 pending message 消费决策更直观character_reply最终完成分支补充 reply-emitted 标记与 pending drain,避免回复已由工具发送时 trace 仍表现为空输出Verification
yarn fast-build core